package com.game.backend.consumer.utils;

import com.alibaba.fastjson2.JSONObject;
import com.game.backend.consumer.WebSocketServer;
import com.game.backend.model.Record;

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.util.List;

public class Game extends Thread{
    private Integer rows;
    private Integer cols;
    private Integer innerWallsCount;
    private int[][] g;
    private final Player playerA,playerB;
    private Integer nextStepA = null;
    private Integer nextStepB = null;
    private ReentrantLock lock = new ReentrantLock();
    private String status = "playing";
    private String loser = ""; // all:平局

    private static final int[] dx = {1,0,-1,0};
    private static final int[] dy = {0,1,0,-1};

    public Game(Integer rows, Integer cols, Integer innerWallsCount, Integer idA, Integer idB) {
        this.rows = rows;
        this.cols = cols;
        this.innerWallsCount = innerWallsCount;
        this.g = new int[rows][cols];
        playerA = new Player(idA,rows-2,1,new ArrayList<>());
        playerB = new Player(idB,1,cols-2,new ArrayList<>());
    }

    public Player getPlayerA() {
        return playerA;
    }

    public Player getPlayerB() {
        return playerB;
    }

    public int[][] getG(){
        return g;
    }

    public void setNextStepA(Integer nextStepA) {
        lock.lock();
        try{
            this.nextStepA = nextStepA;
        }finally {
            lock.unlock();
        }
    }

    public void setNextStepB(Integer nextStepB) {
        lock.lock();
        try{
            this.nextStepB = nextStepB;
        }finally {
            lock.unlock();
        }
    }

    private boolean checkConnectivity(int sx, int sy, int tx, int ty){
        if(sx == tx && sy == ty){
            return true;
        }
        g[sx][sy] = 1;
        for(int i=0;i<4;i++){
            int x = sx+dx[i];
            int y = sy+dy[i];
            if(x>=0 &&  x< rows && y>=0 && y<cols && g[x][y]==0 && checkConnectivity(x,y,tx,ty)){
                g[sx][sy] = 0;
                return true;
            }
        }
        g[sx][sy] = 0;
        return false;

    }

    private boolean draw(){
        for(int i=0;i<this.rows;i++){
            for(int j=0;j<this.cols;j++){
                g[i][j] = 0;
            }
        }

        for(int i=0;i<this.rows;i++){
            g[i][cols-1] = 1;
            g[i][0] = 1;
        }
        for(int i=0;i<this.cols;i++){
            g[rows-1][i] = 1;
            g[0][i] = 1;
        }
        for(int i=0;i<innerWallsCount;i++){
            for(int j=0;j<1000;j++){
                int r = (int)(Math.random() * rows);
                int c = (int)(Math.random() * cols);
                if(g[r][c] == 1)continue;
                if(r == rows-2 && c == 1 || r == 1 && c == cols-2){
                    continue;
                }
                g[r][c] = 1;
                g[rows-1-r][cols-1-c] = 1;
                break;
            }
        }

        return checkConnectivity(this.rows-2,1,1,this.cols-2);
    }

    public void createMap(){
        for(int i=0;i<1000;i++){
            if(draw()){
                break;
            }
        }
    }

    private boolean nextStep(){ // 等待两名玩家的下一步操作
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i=0;i<50;i++){
            try {
                Thread.sleep(100);
                lock.lock();
                try{
                    if(nextStepA != null && nextStepB != null){
                        playerA.getSteps().add(nextStepA);
                        playerB.getSteps().add(nextStepB);
                        return true;
                    }
                }finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private boolean check_valid(List<Cell> cellsA,List<Cell> cellsB){
        int n = cellsA.size();
        Cell cell = cellsA.get(n-1);
        if(g[cell.x][cell.y] == 1)return false;

        for(int i=0;i<n-1;i++){
            if(cellsA.get(i).x == cell.x && cellsA.get(i).y == cell.y){
                return false;
            }
        }

        for(int i=0;i<n-1;i++){
            if(cellsB.get(i).x == cell.x && cellsB.get(i).y == cell.y){
                return false;
            }
        }

        return true;
    }

    private void judge(){
        List<Cell> cellsA = playerA.getCells();
        List<Cell> cellsB = playerB.getCells();

        boolean validA = check_valid(cellsA,cellsB);
        boolean validB = check_valid(cellsB,cellsA);
        if(!validA || !validB){
            status = "finished";

            if(!validA && !validB){
                loser = "all";
            }else if(!validA){
                loser = "A";
            }else if(!validB){
                loser = "B";
            }
        }
    }

    private void sendMove(){//传递移动信息
        lock.lock();
        try{
            JSONObject resp = new JSONObject();
            resp.put("event","move");
            resp.put("a_direction",nextStepA);
            resp.put("b_direction",nextStepB);
            sendAllMessage(resp.toJSONString());
            nextStepA = nextStepB = null;
        }finally {
            lock.unlock();
        }
    }

    private void sendAllMessage(String message){
        WebSocketServer.users.get(playerA.getId()).sendMessage(message);
        WebSocketServer.users.get(playerB.getId()).sendMessage(message);
    }

    private void sendResult(){//传递结果
        JSONObject resp = new JSONObject();
        resp.put("event","result");
        resp.put("loser",loser);
        sendAllMessage(resp.toJSONString());
    }

    private void saveToDatabase(){
        Record record = new Record(
                null,
                playerA.getId(),
                playerA.getSx(),
                playerA.getSy(),
                playerB.getId(),
                playerB.getSx(),
                playerB.getSy(),
                getStepsString(playerA.getSteps()),
                getStepsString(playerB.getSteps()),
                getMapString(),
                loser,
                null
        );
        WebSocketServer.recordMapper.add(record);
    }

    public String getMapString(){
        StringBuilder res = new StringBuilder();
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                res.append(g[i][j]);
            }
        }
        return res.toString();
    }

    public String getStepsString(List<Integer> steps){
        StringBuilder res = new StringBuilder();
        for(int d:steps){
            res.append(d);
        }
        return res.toString();
    }

    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            if(nextStep()){
                judge();
                if("playing".equals(status)){
                    sendMove();
                }else{
                    saveToDatabase();
                    sendResult();
                    break;
                }
            }else{
                status = "finished";
                lock.lock();
                try{
                    if(nextStepA == null && nextStepB == null){ // nextStep没有读到，但这边读到了
                        loser = "all";
                    }else if(nextStepA == null){
                        loser = "A";
                    }else{
                        loser = "B";
                    }
                }finally {
                    lock.unlock();
                }
                saveToDatabase();
                sendResult();
                break;

            }
        }
    }
}
